package com.xqboss.apps.service.order;

import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xqboss.apps.constant.Config;
import com.xqboss.apps.constant.LockKey;
import com.xqboss.apps.domain.order.OrderItem;
import com.xqboss.apps.domain.order.OrderMain;
import com.xqboss.apps.domain.order.PayApply;
import com.xqboss.apps.dto.order.SignPayDto;
import com.xqboss.apps.dto.order.TradeResultDto;
import com.xqboss.apps.enums.order.OrderStatusEnum;
import com.xqboss.apps.enums.order.OrderTypeEnum;
import com.xqboss.apps.enums.order.PayStatusEnum;
import com.xqboss.apps.enums.user.PayTypeEnum;
import com.xqboss.apps.mapper.order.PayApplyMapper;
import com.xqboss.apps.service.order.pay.PayExecutor;
import com.xqboss.apps.vo.pay.PayInfoVo;
import com.xqboss.common.core.redis.lock.LockObject;
import com.xqboss.common.core.redis.lock.LockValue;
import com.xqboss.common.core.redis.lock.RedisLock;
import com.xqboss.common.exception.ServiceException;
import com.xqboss.common.utils.StringUtils;
import com.xqboss.system.service.ISysConfigService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * <p>
 *     申请支付服务
 * </p>
 * @author Trent
 * @date 2023/8/23
 */
@Slf4j
@Service
public class PayApplyService extends ServiceImpl<PayApplyMapper, PayApply> {

    @Autowired
    private Map<String, PayExecutor> payExecutorMap;

    @Autowired
    private OrderService orderService;

    @Autowired
    private OrderItemService orderItemService;

    @Autowired
    private ISysConfigService sysConfigService;

    // 获取用户最后的支付信息
    public PayApply getOrderPayApply(Long orderMainId){
        return getOne(new LambdaQueryWrapper<PayApply>()
                .eq(PayApply::getOrderMainId, orderMainId)
                .orderByDesc(PayApply::getCreateTime)
                .last("LIMIT 1"));
    }

    // 申请支付
    @RedisLock(lockKey = LockKey.LOCK_ORDER_PAY_APPLY)
    public PayInfoVo applyPay(@LockValue Long orderMainId, Long userId, PayTypeEnum payType){
        List<Integer> supportPayType = sysConfigService.getValue(Config.Sys.Order.订单支付方式);
        if(!supportPayType.contains(payType.getValue())){
            throw new ServiceException("支付通道维护中,请选择其他支付方式");
        }
        OrderMain orderMain = orderService.requireOrder(orderMainId, userId);
        if(orderMain.getOrderType() == OrderTypeEnum.Recharge){
            if(payType == PayTypeEnum.Wallet){
                throw new ServiceException("该订单不支持钱包支付！");
            }
        }
        if(orderMain.getPaymentAmount().compareTo(BigDecimal.ZERO) <= 0){
            if(payType != PayTypeEnum.Wallet){
                throw new ServiceException("零元购只支持钱包支付！");
            }
        }
        if(orderMain.getStatus() != OrderStatusEnum.UN_PAY){
            switch (orderMain.getStatus()){
                case CANCEL:
                    throw new ServiceException("订单已取消");
                case CLOSE:
                    throw new ServiceException("订单已关闭");
                case PAYED:
                case COMPLETED:
                    throw new ServiceException("订单已支付，请勿重复付款");
                default:
                    throw new ServiceException("订单状态错误！");
            }
        }
        if(orderMain.getAutoCancelTime().isBefore(LocalDateTime.now())){
            try{
                orderService.cancel(orderMainId, null);
            }catch (Exception e){
            }
            throw new ServiceException("订单支付超时！");
        }
        // 先查询是否已经生成了该订单的支付方式
        List<PayApply> list = list(new LambdaQueryWrapper<PayApply>()
                .eq(PayApply::getOrderMainId, orderMain.getId())
                .orderByDesc(PayApply::getCreateTime)
                .last("LIMIT 1"));
        if(list.size() > 0){
            PayApply payApply = list.get(0);
            if(payApply.getPayStatus() == PayStatusEnum.TRADE_SUCCESS){
                throw new ServiceException("重复支付！");
            }
            if(payApply.getPayStatus() == PayStatusEnum.WAIT_PAY){
                if(payApply.getPayType() == payType){
                    // 相同的支付方式，则直接返回
                    PayInfoVo payInfoVo = new PayInfoVo();
                    payInfoVo.setPayInfo(payApply.getPayInfo());
                    payInfoVo.setTradeId(String.valueOf(payApply.getId()));
                    payInfoVo.setPrice(payApply.getOrderAmount());
                    payInfoVo.setPayType(payApply.getPayType());
                    payInfoVo.setOrderId(payApply.getOrderMainId());
                    return payInfoVo;
                }else{
                    // 取消支付
                    PayExecutor payExecutor = payExecutorMap.get(payApply.getPayType().getCode());
                    payExecutor.cancelPay(payApply);
                    update(new LambdaUpdateWrapper<PayApply>()
                            .set(PayApply::getPayStatus, PayStatusEnum.TRADE_CLOSED)
                            .eq(PayApply::getId, payApply.getId()));
                }
            }
        }
        // 生成新的支付方式
        // 生成支付信息
        PayExecutor payExecutor = payExecutorMap.get(payType.getCode());
        if(Objects.isNull(payExecutor)){
            throw new ServiceException("暂不支持的支付方式！");
        }

        PayApply payApply = new PayApply();
        payApply.setId("JYW" + IdUtil.getSnowflakeNextId());
        payApply.setOrderMainId(orderMain.getId());
        payApply.setUserId(orderMain.getBuyerUserId());
        payApply.setPayType(payType);
        payApply.setPayStatus(PayStatusEnum.WAIT_PAY);
        payApply.setOrderAmount(orderMain.getPaymentAmount());
        // 提前停止支付，防止订单取消问题。
        payApply.setExpireTime(orderMain.getAutoCancelTime().plusSeconds(-2));
        payApply.setIsOrderHandler(false);
        payApply.setOrderType(orderMain.getOrderType());
        OrderTypeEnum orderType = orderMain.getOrderType();
        switch (orderType){
            case Recharge:
                payApply.setSubject("钱包充值");
                break;
            case Box_NFT:
                List<OrderItem> orderItems = orderItemService.getItemsByOrderMain(orderMainId);
                if(!orderItems.isEmpty()){
                    payApply.setSubject(orderItems.get(0).getSpuName());
                }else{
                    payApply.setSubject("购买箱子");
                }
                break;
            default:
                payApply.setSubject("购买商品");
                break;
        }
        SignPayDto signPayDto = payExecutor.signPay(payApply);
        payApply.setOutTradeNo(signPayDto.getOutTradeNo());
        payApply.setPayInfo(signPayDto.getPayInfo());
        payApply.setPayAccountId(signPayDto.getPayAccountId());
        save(payApply);

        PayInfoVo payInfoVo = new PayInfoVo();
        payInfoVo.setPayInfo(payApply.getPayInfo());
        payInfoVo.setTradeId(String.valueOf(payApply.getId()));
        payInfoVo.setPrice(payApply.getOrderAmount());
        payInfoVo.setPayType(payApply.getPayType());
        payInfoVo.setOrderId(payApply.getOrderMainId());
        return payInfoVo;
    }

    // 查询/同步支付状态
    public void syncPayStatus(Long orderMainId){
        List<PayApply> list = list(new LambdaQueryWrapper<PayApply>()
                .eq(PayApply::getOrderMainId, orderMainId)
                .orderByDesc(PayApply::getCreateTime)
                .last("LIMIT 1"));
        if(!list.isEmpty()){
                PayApply payApply = list.get(0);
            if(PayStatusEnum.WAIT_PAY == payApply.getPayStatus()){
                // 待支付状态，同步获取支付状态
                PayExecutor payExecutor = payExecutorMap.get(payApply.getPayType().getCode());
                TradeResultDto trade = payExecutor.queryTrade(payApply);
                paySuccess(trade);
            }else if(PayStatusEnum.TRADE_CLOSED == payApply.getPayStatus()){
                throw new ServiceException("交易已关闭！");
            }
        }else{
            throw new ServiceException("订单未支付！");
        }
    }

    @RedisLock(lockKey = LockKey.LOCK_ORDER_PAY_APPLY)
    public void closePay(@LockValue Long orderMainId){
        // 取消订单支付
        List<PayApply> list = list(new LambdaUpdateWrapper<PayApply>()
                .eq(PayApply::getOrderMainId, orderMainId));
        for (PayApply payApply : list) {
            if(payApply.getPayStatus() == PayStatusEnum.WAIT_PAY){
                PayExecutor executor = payExecutorMap.get(payApply.getPayType().getCode());
                executor.cancelPay(payApply);
                update(new LambdaUpdateWrapper<PayApply>()
                        .set(PayApply::getPayStatus, PayStatusEnum.TRADE_CLOSED)
                        .eq(PayApply::getId, payApply.getId()));
            }
        }
    }

    @RedisLock(lockKey = LockKey.LOCK_ORDER_PAY_APPLY)
    public void paySuccess(@LockObject(valueFields = "tradeNo") TradeResultDto tradeResult){
        // 订单成功
        PayApply payApply = getById(tradeResult.getTradeNo());
        if(payApply != null && payApply.getPayStatus() == PayStatusEnum.WAIT_PAY){
            if(tradeResult.getTotalAmount().compareTo(payApply.getOrderAmount()) != 0){
                log.error("订单金额异常:{}", JSONObject.toJSONString(payApply));
                throw new ServiceException("支付金额异常，请联系客服！");
            }
            payApply.setPayStatus(PayStatusEnum.TRADE_SUCCESS);
            payApply.setBuyerId(tradeResult.getBuyerId());
            payApply.setBuyerAccount(tradeResult.getBuyerAccount());
            payApply.setSellerId(tradeResult.getSellerId());
            payApply.setSellerAccount(tradeResult.getSellerAccount());
            payApply.setOutTradeNo(tradeResult.getOutTradeNo());
            payApply.setPayTime(tradeResult.getPayTime());
            payApply.setPayAmount(tradeResult.getPayAmount());
            payApply.setReceiptAmount(tradeResult.getReceiptAmount());

            LambdaUpdateWrapper<PayApply> wrapper = new LambdaUpdateWrapper<>();
            wrapper.eq(PayApply::getId, payApply.getId());
            wrapper.set(PayApply::getPayStatus, payApply.getPayStatus());
            wrapper.set(PayApply::getPayTime, payApply.getPayTime());
            wrapper.set(PayApply::getBuyerId, payApply.getBuyerId());
            wrapper.set(PayApply::getBuyerAccount, payApply.getBuyerAccount());
            wrapper.set(PayApply::getSellerId, payApply.getSellerId());
            wrapper.set(PayApply::getSellerAccount, payApply.getSellerAccount());
            wrapper.set(PayApply::getOutTradeNo, payApply.getOutTradeNo());
            wrapper.set(PayApply::getPayAmount, payApply.getPayAmount());
            wrapper.set(PayApply::getReceiptAmount, payApply.getReceiptAmount());
            boolean update = update(wrapper);
            if(!update){
                throw new ServiceException("系统忙！");
            }
            try{
                orderService.payed(payApply);
                update(new LambdaUpdateWrapper<PayApply>()
                        .eq(PayApply::getId, payApply.getId())
                        .set(PayApply::getIsOrderHandler, true));
            }catch (Exception e){
                log.error("订单状态处理异常:" + payApply.getOrderMainId(), e);
                // 标记订单异常
                orderService.exception(payApply.getOrderMainId());
            }
        }
    }

    /**
     * 获取订单的支付记录
     */
    public List<PayApply> getOrderPayList(Long orderId) {
        return list(new LambdaQueryWrapper<PayApply>()
                .eq(PayApply::getOrderMainId, orderId));
    }
}
